package com.tutorial.demo4functionalinterface;

import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @author xiaoxiaokui
 * @description: Test
 * @date 2019/10/29
 **/
public class Test {
    public static void main(String[] args) {

        Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
        Integer coverted = converter.convert("123");
        System.out.println(coverted);
        // 注意：上面的示例代码，即使去掉 @FunctionalInterface 也是好使的，
        // 它仅仅是一种约束而已。



        /**-------------Lambda 便捷的引用类的构造器及方法start---------------*/
        // 上面这段代码，通过 Java 8 的新特性，进一步简化上面的代码：
        // Java 8 中允许你通过 :: 关键字来引用类的方法或构造器
        Converter<String, Integer> converter2 = Integer::valueOf;
        Integer converted2 = converter2.convert("123");
        System.out.println(converted2);

        // 上面的代码简单的示例了如何引用静态方法，
        // 当然，除了静态方法，我们还可以引用普通方法：
        Something something = new Something();
        Converter<String, String> converter3 = something::startsWith;
        String converted3 = converter3.convert("Java");
        System.out.println(converted3);

        // 接下来，我们再来看看如何通过 :: 关键字来引用类的构造器。
        // 首先，我们先来定义一个示例类（com.tutorial.demo4functionalinterface.Person），在类中声明两个构造器。
        // 然后，我们再定义一个工厂接口（com.tutorial.demo4functionalinterface.PersonFactory），用来生成 Person 类。
        // 我们可以通过 :: 关键字来引用 Person 类的构造器，来代替手动去实现这个工厂接口：
        // 直接引用 Person 构造器
        PersonFactory<Person> personFactory = Person::new;
        Person person = personFactory.create("Peter", "Parker");
        System.out.println(person);
        // Person::new 这段代码，能够直接引用 Person 类的构造器。
        // 然后 Java 编译器能够根据上下文选中正确的构造器去实现 PersonFactory.create 方法。
        /**-------------Lambda 便捷的引用类的构造器及方法end-----------------*/



        /**-------------Lambda 访问外部变量及接口默认方法start---------------*/
        // 在本章节中，我们将会讨论如何在 lambda 表达式中访问外部变量
        // （包括：局部变量，成员变量，静态变量，接口的默认方法.），它与匿名内部类访问外部变量很相似。


        ///// 访问局部变量 \\\\\
        // 在 Lambda 表达式中，我们可以访问外部的 final 类型变量
        final int num = 1;
        Converter<Integer, String> stringConverter = (from -> String.valueOf(from + num));
        System.out.println(stringConverter.convert(2));

        // 与匿名内部类不同的是，我们不必显式声明 num 变量为 final 类型，下面这段代码同样有效：
        int num2 = 1;
        Converter<Integer, String> stringConverter2 = (from) -> String.valueOf(from + num2);
        System.out.println(stringConverter2.convert(2));

        // 但是 num 变量必须为隐式的 final 类型，
        // 何为隐式的 final 呢？就是说到编译期为止，num 对象是不能被改变的，
        // 如下面这段代码，就不能被编译通过：
        int num3 = 1;
        Converter<Integer, String> stringConverter3 = (from) -> String.valueOf(from + num3);
//        num3 = 3;

        // 在 lambda 表达式内部改变 num 值同样编译不通过，
        // 需要注意, 比如下面的示例代码：
        int num4 = 1;
//        Converter<Integer, String> stringConverter4 = (from) -> {
//            String value = String.valueOf(from + num4);
//            num4 = 3;
//            return value;
//        };


        ///// 访问成员变量和静态变量 \\\\\
        // 上一章节中，了解了如何在 Lambda 表达式中访问局部变量。
        // 与局部变量相比，在 Lambda 表达式中对成员变量和静态变量拥有读写权限：
        // 详细 参考 com.tutorial.demo4functionalinterface.Lambda4


        ///// 访问接口的默认方法 \\\\\
//        Formula formula = (a) -> sqrt(a * 100);
        // 带有默认实现的接口方法，是不能在 lambda 表达式中访问的，
        // 上面这段代码将无法被编译通过。


        ///// 内置的函数式接口 \\\\\
        // JDK 1.8 API 包含了很多内置的函数式接口。
        // 其中就包括我们在老版本中经常见到的 Comparator 和 Runnable，
        // Java 8 为他们都添加了 @FunctionalInterface 注解，
        // 以用来支持 Lambda 表达式。

        // 1、Predicate 断言
        // Predicate 是一个可以指定入参类型，并返回 boolean 值的函数式接口。
        // 它内部提供了一些带有默认实现的方法，
        // 可以被用来组合一个复杂的逻辑判断（and, or, negate）
        Predicate<String> predicate = (s) -> s.length() > 0;

        System.out.println(predicate.test("foo"));
        System.out.println(predicate.negate().test("foo"));

        Predicate<Boolean> nonNull = Objects::nonNull;
        boolean ab = true;
        System.out.println(nonNull.test(ab));
        Predicate<Boolean> isNull = Objects::isNull;
        boolean bb = true;
        System.out.println(isNull.test(bb));

        Predicate<String> isEmpty = String::isEmpty;
        System.out.println(isEmpty.test("abc"));
        Predicate<String> isNotEmpty = isEmpty.negate();
        System.out.println(isNotEmpty.test("abc"));

        // 2、Function
        // Function 函数式接口的作用是，我们可以为其提供一个原料，
        // 他给生产一个最终的产品。
        // 通过它提供的默认方法，组合,链行处理(compose, andThen)
        Function<String, Integer> toInteger = Integer::valueOf;
        Function<String, String> backToString = toInteger.andThen(String::valueOf);
        System.out.println(backToString.apply("123"));

        // 3、Supplier 生产者
        // Supplier 与 Function 不同，它不接受入参，
        // 直接为我们生产一个指定的结果，有点像生产者模式
        Supplier<Person> personSupplier = Person::new;
        System.out.println(personSupplier.get());

        // 4、Consumer 消费者
        // 对于 Consumer，我们需要提供入参，用来被消费，如下面这段示例代码
        Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
        greeter.accept(new Person("Luke", "Skywalker"));

        // 5、Comparator
        // Comparator 在 Java 8 之前是使用比较普遍的。
        // Java 8 中除了将其升级成了函数式接口，还为它展了一些默认方法
        Comparator<Person> comparator = Comparator.comparing(p -> p.firstName);
        Person p1 = new Person("John", "Doe");
        Person p2 = new Person("Alice", "Wonderland");
        System.out.println(comparator.compare(p1, p2));
        System.out.println(comparator.reversed().compare(p1, p2));

        // 6、Optional
        // 首先，Optional 它不是一个函数式接口，
        // 设计它的目的是为了防止空指针异常（NullPointerException），
        // 要知道在 Java 编程中， 空指针异常可是臭名昭著的。

        // 让我们来快速了解一下 Optional 要如何使用！
        // 你可以将 Optional 看做是包装对象
        // （可能是 null, 也有可能非 null）的容器。
        // 当你定义了一个方法，这个方法返回的对象可能是空，
        // 也有可能非空的时候，你就可以考虑用 Optional 来包装它，
        // 这也是在 Java 8 被推荐使用的做法。
        Optional<String> optional = Optional.of("bam");
        System.out.println(optional.isPresent());
        System.out.println(optional.get());
        System.out.println(optional.orElse("fallback"));
        optional.ifPresent((s) -> System.out.println(s.charAt(0)));

        /**-------------Lambda 访问外部变量及接口默认方法end-----------------*/

    }
}
